﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
// 宏定义：当前年份（用于计算年龄）
#define CURRENT_YEAR 2023

// 身份证前17位对应的加权因子（国家标准GB11643-1999规定）
// 索引0-16分别对应前17位数字的权重
int weights[] = { 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 };

// 校验码映射表（模11后的余数0-10分别对应如下字符）
// 例如：余数为2时，校验码为'X'（代表10）
char checkCodes[] = { '1','0','X','9','8','7','6','5','4','3','2' };

/**
 * 函数功能：计算一个整数的各位数字与对应权重的乘积之和
 * 参数说明：
 *   num         - 待处理的整数（如地址码、年份、月份等）
 *   digitCount  - 该整数的位数（如地址码是6位，年份是4位）
 *   startIndex  - 权重数组中开始取值的索引（对应前17位中的位置）
 * 返回值：       各位数字与对应权重相乘后的总和
 */
int calculateSum(int num, int digitCount, int startIndex)
{
    int sum = 0;                  // 累加结果
    int digits[6];                // 临时存储各位数字（最多6位，满足所有分段需求）
    int i;                        // 循环变量

    // 第一步：提取整数的每一位（从个位开始，存到digits数组）
    // 例如：num=110101（6位），循环后digits[0]=1, digits[1]=0, digits[2]=1, digits[3]=0, digits[4]=1, digits[5]=1
    for (i = 0; i < digitCount; i++)
    {
        digits[i] = num % 10;     // 取最后一位（个位）
        num = num / 10;           // 去掉最后一位
    }

    // 第二步：按原数字顺序（从高位到低位）与对应权重相乘并累加
    // 例如：digits存储的是[1,0,1,0,1,1]（实际原数字是110101），需要反转顺序为1,1,0,1,0,1
    for (i = 0; i < digitCount; i++)
    {
        // 取反转后的数字位：digits[digitCount-1 - i]
        // 乘以权重数组中对应的权重：weights[startIndex + i]
        sum += digits[digitCount - 1 - i] * weights[startIndex + i];
    }

    return sum;                   // 返回该分段的加权和
}

/**
 * 主函数：实现身份证号校验、出生日期提取、年龄计算功能
 * 流程：输入身份证号 → 拆分各分段 → 计算加权和 → 验证校验码 → 输出结果
 */
int main()
{
    int areaCode;     // 地址码（前6位）
    int year;         // 出生年份（接下来4位）
    int month;        // 出生月份（接下来2位）
    int day;          // 出生日期（接下来2位）
    int orderCode;    // 顺序码（接下来3位）
    char checkCode;   // 校验码（最后1位，可能是数字或'X'）
    int totalSum = 0; // 前17位所有数字的加权和总和
    int mod;          // 加权和除以11的余数
    char rightCode;   // 计算得出的正确校验码

    // 提示用户输入18位身份证号
    printf("请输入18位身份证号：");
    // 按格式拆分输入：6位地址码+4位年+2位月+2位日+3位顺序码+1位校验码
    scanf("%6d%4d%2d%2d%3d%c", &areaCode, &year, &month, &day, &orderCode, &checkCode);

    // 计算前17位的总加权和（分段计算后累加）
    totalSum += calculateSum(areaCode, 6, 0);    // 地址码6位，对应权重索引0-5
    totalSum += calculateSum(year, 4, 6);        // 年份4位，对应权重索引6-9
    totalSum += calculateSum(month, 2, 10);      // 月份2位，对应权重索引10-11
    totalSum += calculateSum(day, 2, 12);        // 日期2位，对应权重索引12-13
    totalSum += calculateSum(orderCode, 3, 14);  // 顺序码3位，对应权重索引14-16

    // 计算校验码：总加权和 % 11 得到余数，再查映射表
    mod = totalSum % 11;
    rightCode = checkCodes[mod];

    // 对比输入的校验码和计算出的正确校验码
    if (rightCode == checkCode)
    {
        printf("校验通过！\n");
        // 输出出生日期（%02d确保月份和日期为2位，如1月显示为01）
        printf("出生日期：%d-%02d-%02d\n", year, month, day);
        // 计算年龄（当前年份 - 出生年份）
        printf("年龄：%d岁\n", CURRENT_YEAR - year);
    }
    else
    {
        printf("校验不通过！正确校验码应为：%c\n", rightCode);
    }

    return 0;
}